import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, Component, ElementRef, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { OverlaysModule } from './overlay.module';
import { OverlayService } from './overlay.service';
import { TemplatePortal } from '@angular/cdk/portal';
import { ButtonModule } from '@iux/live';
import { Overlay, OverlayConfig } from '@angular/cdk/overlay';

@Component({
  template: `
    <button #button lv-button (click)="openOverlay()">Open</button>
    <ng-template #template>
      <div class="ui-overflow">
        This is Content...
        <br />
        This is Content...
        <br />
        This is Content...
        <br />
        <button lv-button lvSize="auto" lvType="link" (click)="close()">Close</button>
      </div>
    </ng-template>
  `,
  styles: [
    `
      .ui-overflow {
        width: 230px;
        margin-top: 4px;
        padding: 16px;
        border-radius: 8px;
        background: #ffffff;
        box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.1);
      }

      .ui-overflow [lv-button] {
        float: right;
      }
    `,
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestOverlayComponent {
  @Input() withCustom: boolean = false;
  @Input() extendConfig;
  @ViewChild('template', { static: false }) template: TemplateRef<any>;
  @ViewChild('button', { read: ElementRef, static: false }) button: ElementRef<any>;

  overlayRef;

  constructor(private viewContainerRef: ViewContainerRef, private overlayService: OverlayService, public overlay: Overlay) {}

  openOverlay() {
    if (!this.overlayRef || !this.overlayRef.overlayRef.ha2sAttached()) {
      const portal = new TemplatePortal(this.template, this.viewContainerRef);
      if (this.extendConfig) {
        this.overlayRef = this.overlayService.open(portal, this.getDefaultConfig(), this.extendConfig);
      } else if (this.withCustom) {
        this.overlayRef = this.overlayService.open(portal, this.getDefaultConfig());
      } else {
        this.overlayRef = this.overlayService.open(portal);
      }
    }
  }

  getDefaultConfig() {
    return new OverlayConfig({
      hasBackdrop: false,
      positionStrategy: this.overlay
        .position()
        .flexibleConnectedTo(this.button)
        .withPositions([
          {
            originX: 'start',
            originY: 'bottom',
            overlayX: 'start',
            overlayY: 'top',
          },
          {
            originX: 'start',
            originY: 'top',
            overlayX: 'start',
            overlayY: 'bottom',
          },
        ]),
    });
  }

  close() {
    this.overlayRef.close();
  }
}

describe('overlay', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestOverlayComponent],
      imports: [OverlaysModule, ButtonModule],
      providers: [OverlayService],
    }).compileComponents();
  }));

  describe('works', () => {
    let component: TestOverlayComponent;
    let fixture: ComponentFixture<TestOverlayComponent>;

    beforeEach(() => {
      fixture = TestBed.createComponent(TestOverlayComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });

    it('with open', fakeAsync(() => {
      const button = fixture.nativeElement.querySelector('.lv-button');
      button.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const overlay = document.querySelector('.cdk-overlay-container');
      expect(overlay).toMatchSnapshot();
    }));

    it('with close', fakeAsync(() => {
      const openBtn = fixture.nativeElement.querySelector('.lv-button');
      openBtn.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const overlay = document.querySelector('.cdk-overlay-container');
      const closeBtn = overlay.querySelector('.lv-button');
      closeBtn.dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeNull();
    }));

    it('with custom config', fakeAsync(() => {
      component.withCustom = true;
      tick(500);
      fixture.detectChanges();
      const openBtn = fixture.nativeElement.querySelector('.lv-button');
      openBtn.dispatchEvent(new Event('click'));
      tick(1000);
      fixture.detectChanges();
      const overlay = document.querySelector('.cdk-overlay-container');
      expect(overlay).toMatchSnapshot();
      expect(overlay.querySelector('.cdk-overlay-backdrop')).toBeNull();
    }));

    it('with beforeOpen', fakeAsync(() => {
      const fn = jest.fn();
      component.extendConfig = {
        backdropBindClose: true,
        beforeOpen: () =>
          new Promise<void>(resolve => {
            setTimeout(() => {
              fn();
            }, 500);
            setTimeout(() => {
              resolve();
            }, 1000);
          }),
      };
      const button = fixture.nativeElement.querySelector('.lv-button');
      button.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(fn).toBeCalledTimes(1);
      expect(document.querySelector('.ui-overflow')).toBeNull();
      tick(1000);
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeTruthy();
    }));

    it('with afterOpen', fakeAsync(() => {
      const fn = jest.fn();
      component.extendConfig = {
        backdropBindClose: true,
        afterOpen: () => {
          setTimeout(() => {
            fn();
          }, 500);
        },
      };
      const button = fixture.nativeElement.querySelector('.lv-button');
      button.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeTruthy();
      expect(fn).toBeCalledTimes(0);
      tick(500);
      fixture.detectChanges();
      expect(fn).toBeCalledTimes(1);
    }));

    it('with beforeClose', fakeAsync(() => {
      const fn = jest.fn();
      component.extendConfig = {
        backdropBindClose: true,
        beforeClose: () =>
          new Promise<void>(resolve => {
            setTimeout(() => {
              fn();
            }, 500);
            setTimeout(() => {
              resolve();
            }, 1000);
          }),
      };
      const openBtn = fixture.nativeElement.querySelector('.lv-button');
      openBtn.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      const overlay = document.querySelector('.cdk-overlay-container');
      const closeBtn = overlay.querySelector('.lv-button');
      closeBtn.dispatchEvent(new Event('click'));
      expect(document.querySelector('.ui-overflow')).toBeTruthy();
      expect(fn).toBeCalledTimes(0);
      tick(500);
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeTruthy();
      expect(fn).toBeCalledTimes(1);
      tick(1000);
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeNull();
    }));

    it('with afterClose', fakeAsync(() => {
      const fn = jest.fn();
      component.extendConfig = {
        backdropBindClose: true,
        afterClose: () => {
          setTimeout(() => {
            fn();
          }, 500);
        },
      };
      const openBtn = fixture.nativeElement.querySelector('.lv-button');
      openBtn.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      const overlay = document.querySelector('.cdk-overlay-container');
      const closeBtn = overlay.querySelector('.lv-button');
      closeBtn.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(document.querySelector('.ui-overflow')).toBeNull();
      expect(fn).toBeCalledTimes(0);
      tick(500);
      fixture.detectChanges();
      expect(fn).toBeCalledTimes(1);
    }));
  });
});
